package com.shiva.oms.server;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sf.shiva.oms.common.constant.LogParseConfigEnum;
import com.sf.shiva.oms.common.constant.LogTableConstant;
import com.sf.shiva.oms.common.util.CollectionUtil;
import com.shiva.oms.logview.api.model.TableColumns;
import com.shiva.oms.logview.domain.LogData;
import com.shiva.oms.logview.domain.ParsingContext;
import com.shiva.oms.server.parser.Log4jPatternMultilineLogParser;
import com.shiva.oms.server.parser.MultiLineLogParser;


/**
 * 
 * 服务层方法，包括初始化日志配置、读取和解析日志文件、分类过滤等
 * 
 * <pre>
 * HISTORY
 * ****************************************************************************
 *  ID    DATE            PERSON            REASON
 *  1     2018年3月30日         LA.01367604           Create
 * ****************************************************************************
 * </pre>
 * 
 * @author LA.01367604
 * @version 1.0
 */
public class LogViewerParserService {

    private static final Logger logger = LoggerFactory.getLogger(LogViewerParserService.class);

    // private Log4jPatternMultilineLogParser log4jParser;
    // private LogImporterUsingParser logParser;
    // private LogDataCollector coll;
    private Map<String, Log4jPatternMultilineLogParser> parserBeans = new HashMap<>();
    private volatile boolean parsingInProgress = false;
    
    private static final String GROUP_ATTR = "group";
    
    private TableColumns[] headNames;
    private Properties properties;
    private static String readAndParseLine = null;
    private BufferedReader logReader;

    private Properties createProperties(String type, String pattern, String dateFormat, String name, String charset){
        Properties pros = new Properties();
        pros.setProperty("log.viewer.type", type);
        pros.setProperty("log.viewer.pattern", pattern);
        pros.setProperty("log.viewer.dateFormat", dateFormat);
        pros.setProperty("log.viewer.name", name);
        pros.setProperty("log.viewer.charset", charset);
        return pros;
    }
    
    public Log4jPatternMultilineLogParser init(String type, String pattern, String dateFormat, String name, String charset) throws Exception {
        Properties properties = createProperties(type, pattern, dateFormat, name, charset);
        Log4jPatternMultilineLogParser log4jParser = new Log4jPatternMultilineLogParser();
        log4jParser.init(properties);
        return log4jParser;
    }
    
    public Log4jPatternMultilineLogParser init(LogParseConfigEnum cfg) throws Exception {
        return init(cfg.getType(), cfg.getPattern(), cfg.getDateFormat(), cfg.getLogName(), cfg.getCharset());
    }

    private void init() throws Exception {
        if (parserBeans.isEmpty()) {
            for (LogParseConfigEnum name : LogParseConfigEnum.values()) {
                Log4jPatternMultilineLogParser parser = init(name);
                parserBeans.put(name.getCfgName(), parser);
            }
        }
//        this.categoryNames.clear();
    }

    private BufferedReader createReader(InputStream in, String charset) {
        BufferedReader logReader = null;
        if (charset == null) {
            logReader = new BufferedReader(new InputStreamReader(in));
        } else {
            try {
                logReader = new BufferedReader(new InputStreamReader(in, charset));
            } catch (UnsupportedEncodingException e1) {
                logger.error(String.format("Required charset [%s] is not supported: %s", charset,e1.getMessage()));
                logger.info(String.format("Using default charset: %s", Charset.defaultCharset().displayName()));
                logReader = new BufferedReader(new InputStreamReader(in));
            }
        }
        return logReader;
    }

    public List<LogData> readAndParse(InputStream inputData, Log4jPatternMultilineLogParser parserBean, String charset, ParsingContext parsingContext) {
        if (inputData == null) {
            return new ArrayList<>();
        }
        long startTime = System.currentTimeMillis(); // 获取开始时间
        String line = readAndParseLine;
        readAndParseLine = null;
        LogData logData;
        if (logReader == null) {
            logReader = createReader(inputData, charset);
        }
        List<LogData> dataCollector = new ArrayList<>();

        int lineCount = 0;
        while (true) {
//            synchronized (parsingContext) {
//                if (!parsingContext.isParsingInProgress()) {
//                    break;
//                }
//            }
            try {
                if (lineCount == 0 && null == line) {
                    line = logReader.readLine();
                }
                if (null == line) {
                    break;
                }
                line = line.replace('\u0000', ' ').replace("  ", " "); // 去掉空白符和多余空格
                if (++lineCount == 1 && !parserBean.isMatchRegexpPattern(line)) { // 匹配第一行，如果不成功说明模板错误，不必继续读
                    readAndParseLine = line;
                    break;
                }
                // synchronized (parsingContext) {
                logData = parserBean.parse(line, parsingContext);
                // }

                if (null != logData) {
                    logData.setId(parsingContext.getGeneratedIdAndIncrease());
                    dataCollector.add(logData);
                    parsingContext.setLastParsed(System.currentTimeMillis());
                    // setGroupIndex(logData);
                }
                line = logReader.readLine();

            } catch (Exception e) {
                logger.error(String.format("Exception during log import: %s", e.getMessage()));
                break;
            }
        }

        parseBuffer(parserBean, dataCollector, parsingContext);
        // IOUtils.closeQuietly(logReader);
        long endTime = System.currentTimeMillis(); // 获取结束时间
        System.out.println("读取" + lineCount + "行, 运行时间：" + (endTime - startTime) + "ms"); // 输出程序运行时间
        return dataCollector;
    }

    /**
     * 整理解析缓冲中剩余的数据
     */
    private void parseBuffer(Log4jPatternMultilineLogParser parserBean, List<LogData> dataCollector, ParsingContext parsingContext) {
        try {
            MultiLineLogParser multiLineLogParser = (MultiLineLogParser) parserBean;
            LogData logData = multiLineLogParser.parseBuffer(parsingContext);
            if (logData != null) {
                logData.setId(parsingContext.getGeneratedIdAndIncrease());
                // synchronized (parsingContext) {
                    dataCollector.add(logData);
                // }
                parsingContext.setLastParsed(System.currentTimeMillis());
//                setGroupIndex(logData);
            }
        } catch (Exception e) {
            logger.info("Cannot parser rest of buffer, probably stopped importing");
        }
    }

    public List<LogData> parse(InputStream inputData) throws Exception {
        this.parsingInProgress = true;
        init();
        List<LogData> list = new ArrayList<>();
        for (Log4jPatternMultilineLogParser bean : parserBeans.values()) {
            String charset= bean.getProperties().getProperty(LogTableConstant.PARSING_PROPERTY_CHARSET);
            ParsingContext parsingContext = new ParsingContext();
            bean.initParsingContext(parsingContext);
            list = readAndParse(inputData, bean, charset, parsingContext);
            if (CollectionUtil.isNotEmpty(list)) {
                this.headNames = bean.getTableColumnsToUse();
                this.properties = bean.getProperties();
                break;
            }
        }
        this.logReader = null;
        this.parsingInProgress = false;
        return list;
    }

//    private void setGroupIndex(LogData data) {
//        Object attrIndex = this.categoryNames.get(LogTableConstant.LEVEL_ATTR);
//        if (attrIndex == null) {
//            attrIndex = ArrayListMultimap.create();
//        }
//        ((Multimap<String, LogData>) attrIndex).put(data.getLevel().toString(), data);
//        this.categoryNames.put(LogTableConstant.LEVEL_ATTR, attrIndex);
//    }

    /**
     * 根据日志级别过滤数据
     * @param list
     * @param filterLevel
     * @return
     * @author LA.01367604-2018年4月18日
     */
    public List<LogData> filterByLevel(List<LogData> list, List<String> filterLevel) {
        long startTime = System.currentTimeMillis(); // 获取开始时间
        List<LogData> res = Collections.emptyList();
        if (list.isEmpty()) {
            return res;
        }
        if (filterLevel.isEmpty()) { // 如果未选择级别，则默认除这几个级别外的日志数据
            res = list.parallelStream()
                    .filter( data -> !(LogTableConstant.LEVEL_ERROR.equals(data.getLevel().toString())
                                || LogTableConstant.LEVEL_WARN.equals(data.getLevel().toString())
                                || LogTableConstant.LEVEL_INFO.equals(data.getLevel().toString())
                                || LogTableConstant.LEVEL_DEBUG.equals(data.getLevel().toString())))
                    .collect(Collectors.toList());
        } else {
            res= list.parallelStream().filter(data-> {
                for(String level:filterLevel){
                    if (level.equals(data.getLevel().toString())){
                        return true;
                    }
                }
                return false;
            }).collect(Collectors.toList());
        }
        long endTime = System.currentTimeMillis(); // 获取结束时间
        System.out.println("过滤运行时间：" + (endTime - startTime) + "ms"); // 输出程序运行时间
        return res;
    }

    public List<LogData> filterByClass(List<LogData> list, String clazzName) {
        long startTime = System.currentTimeMillis(); // 获取开始时间
        if (list.isEmpty() || StringUtils.isBlank(clazzName)) {
            return list;
        }
        List<LogData> res = list.parallelStream()
                .filter(data -> !clazzName.equals(data.getClazz())).collect(Collectors.toList());
        long endTime = System.currentTimeMillis(); // 获取结束时间
        System.out.println("过滤运行时间：" + (endTime - startTime) + "ms"); // 输出程序运行时间
        return res;
    }
    
    public List<LogData> filterByKeyword(List<LogData> list, String keyword) {
        long startTime = System.currentTimeMillis(); // 获取开始时间
        if (list.isEmpty() || StringUtils.isBlank(keyword)) {
            return list;
        }
        List<LogData> res = list.parallelStream()
                .filter(data -> data.toText().contains(keyword)).collect(Collectors.toList());
        long endTime = System.currentTimeMillis(); // 获取结束时间
        System.out.println("查找运行时间：" + (endTime - startTime) + "ms"); // 输出程序运行时间
        return res;
    }

    public List<LogData> filterByTime(List<LogData> list, Date startDate, Date endDate) {
        long startTime = System.currentTimeMillis(); // 获取开始时间
        if (list.isEmpty()) {
            return list;
        }
        List<LogData> res = list.parallelStream()
                .filter(data->(0==data.getDate().compareTo(startDate) || 0== data.getDate().compareTo(endDate) || (data.getDate().after(startDate) && data.getDate().before(endDate))))
                .collect(Collectors.toList());
        long endTime = System.currentTimeMillis(); // 获取结束时间
        System.out.println("筛选运行时间：" + (endTime - startTime) + "ms"); // 输出程序运行时间
        return res;
    }

    public TableColumns[] getHeadNames() {
        return headNames;
    }

    public Properties getProperties() {
        return properties;
    }

    public boolean isParsingInProgress() {
        return parsingInProgress;
    }

    public void setParsingInProgress(boolean parsingInProgress) {
        this.parsingInProgress = parsingInProgress;
    }

}
